package myError

import (
	"fmt"
	"gitee.com/super_step/go_utils/iris/service_code"
	"runtime"
)

type Func struct {
	File     string
	Line     int
	FuncName string
}

type Error struct {
	msgs   []string
	where  []Func
	code   int32
	topMsg string
}

func (e *Error) Error() string {
	return fmt.Sprintf("{\"where\":\"%v\", \"msg\": \"%v\", \"code\": %d}",
		e.where, e.msgs, e.code)
}

func (e *Error) GetMsg() []string {
	return e.msgs
}

func (e *Error) GetTopMsg() string {
	return e.topMsg
}

func (e *Error) GetWhere() []Func {
	return e.where
}

func (e *Error) GetCode() int32 {
	return e.code
}

func New(format string, args ...interface{}) *Error {
	return _new(format, args...)
}

func _new(format string, args ...interface{}) *Error {
	msg := getFormat(format, args)
	return &Error{
		where:  []Func{getWhere(3)},
		code:   service_code.UnknownError,
		msgs:   []string{msg},
		topMsg: msg,
	}
}

func NewWithCode(code int32, format string, args ...interface{}) *Error {
	tmpErr := _new(format, args...)
	tmpErr.code = code
	return tmpErr
}

func getWhere(skip int) Func {
	pc, file, line, _ := runtime.Caller(skip)
	pcName := runtime.FuncForPC(pc).Name()
	return Func{
		File:     file,
		Line:     line,
		FuncName: pcName,
	}
}

func getFormat(format string, args []interface{}) string {
	if len(args) > 0 {
		format = fmt.Sprintf(format, args...)
	}
	return format
}

func Warp(err error, format string, args ...interface{}) *Error {
	return _warp(err, format, args...)
}

func _warp(err error, format string, args ...interface{}) *Error {
	if err == nil {
		return New(format, args...)
	}
	where := getWhere(3)
	format = getFormat(format, args)
	switch t := err.(type) {
	case *Error:
		// 继承where
		t.where = append(t.where, where)
		// 添加本次错误信息
		t.msgs = append(t.msgs, format)
		return t
	default:
		return &Error{
			msgs:   []string{err.Error(), format},
			where:  []Func{where},
			code:   service_code.UnknownError,
			topMsg: format,
		}
	}
}

func WarpWithCode(err error, code int32, format string, args ...interface{}) *Error {
	tmpErr := _warp(err, format, args...)
	if tmpErr.code == service_code.UnknownError {
		tmpErr.code = code
	}
	return tmpErr
}
